#include "drivers/chip/uart.h"
#include "CMSIS/CMSDK_CM3.h"

#define SYS_LOG_DOMAIN "qemu"
#include "sys_log.h"

#include "board_config.h"

#define UART0_ADDR (0x40004000ul)
#define UART0 ((UART_t *)(UART0_ADDR))

typedef struct UART_t
{
    volatile uint32_t DATA;

    volatile struct
    {
        volatile uint32_t TX_FULL : 1;
        volatile uint32_t RX_FULL : 1;
        volatile uint32_t TX_OVERRUN : 1;
        volatile uint32_t RX_OVERRUN : 1;
    } STATE;

    volatile struct
    {
        volatile uint32_t TX_ENABLE : 1;
        volatile uint32_t RX_ENABLE : 1;
        volatile uint32_t TX_INT_ENABLE : 1;
        volatile uint32_t RX_INT_ENABLE : 1;
        volatile uint32_t TX_OVERRUN_INT_ENABLE : 1;
        volatile uint32_t RX_OVERRUN_INT_ENABLE : 1;
    } CTRL;

    volatile struct
    {
        volatile uint32_t TX : 1;
        volatile uint32_t RX : 1;
        volatile uint32_t TX_OVERRUN : 1;
        volatile uint32_t RX_OVERRUN : 1;
    } INTSTATUS;

    volatile uint32_t BAUDDIV;
} UART_t;

static void _isr_default(void) {}

static uart_isr_cb_fn uart_rx_irq[] = {
    [0] = _isr_default,
};

static uart_isr_cb_fn uart_tx_irq[] = {
    [0] = _isr_default,
};

void UART0_Receive_Handler(void)
{
    UART0->INTSTATUS.RX = 1;
    uart_rx_irq[0]();
}

void UART0_Transmit_Handler(void)
{
    UART0->INTSTATUS.TX = 1;
    uart_tx_irq[0]();
}

void drv_uart_pin_configure_txd(hal_id_t id, uint8_t pin)
{
}

void drv_uart_pin_configure_rxd(hal_id_t id, uint8_t pin)
{
}

void drv_uart_enable(hal_id_t id)
{
}

void drv_uart_disable(hal_id_t id)
{
}

void drv_uart_init(hal_id_t id, const uart_param_t *param)
{
    UART0->BAUDDIV = 1;
    UART0->CTRL.TX_ENABLE = 1;
    UART0->CTRL.RX_ENABLE = 1;
}

void drv_uart_deinit(hal_id_t id)
{
}

void drv_uart_set_tx_buffer(hal_id_t id, void *buf, unsigned size)
{
    SYS_LOG("");
}

void drv_uart_set_rx_buffer(hal_id_t id, void *buf, unsigned size)
{
    SYS_LOG("");
}

int drv_uart_get_tx_buffer_available(hal_id_t id)
{
    SYS_LOG("");
    return -1;
}

int drv_uart_get_rx_data_available(hal_id_t id)
{
    SYS_LOG("");
    return -1;
}

void drv_uart_set_br(hal_id_t id, unsigned clk, unsigned baudrate)
{
    SYS_LOG("");
}

unsigned drv_uart_get_br(hal_id_t id, unsigned clk)
{
    SYS_LOG("");
    return 0;
}

int drv_uart_poll_read(hal_id_t id, void *data)
{
    if (UART0->STATE.RX_FULL)
    {
        ((char *)data)[0] = UART0->DATA;
        return 1;
    }
    else
    {
        return 0;
    }
}

int drv_uart_poll_write(hal_id_t id, uint8_t data)
{
    UART0->DATA = data;
    return 0;
}

int drv_uart_fifo_read(hal_id_t id, void *data, int size)
{
    int ret = 0;
    for (int i = 0; i < size; i++)
    {
        if (drv_uart_poll_read(id, &((char *)data)[i]) <= 0)
        {
            break;
        }
        ++ret;
    }
    return ret;
}

int drv_uart_fifo_fill(hal_id_t id, const void *data, int size)
{
    for (unsigned i = 0; i < size; i++)
    {
        drv_uart_poll_write(id, ((char *)data)[i]);
    }
    return size;
}

int drv_uart_irq_callback_enable(hal_id_t id, uart_isr_cb_fn cb)
{
    SYS_LOG("");
    uart_rx_irq[0] = cb;
    uart_tx_irq[0] = cb;
    return 0;
}

int drv_uart_irq_callback_disable(hal_id_t id)
{
    SYS_LOG("");
    uart_rx_irq[0] = _isr_default;
    uart_tx_irq[0] = _isr_default;
    return 0;
}

void drv_uart_irq_enable(hal_id_t id, bool rx, bool tx, int priority)
{
    SYS_LOG("");
    if (rx)
        UART0->CTRL.RX_INT_ENABLE = 1;
    if (tx)
        UART0->CTRL.TX_INT_ENABLE = 1;
    NVIC_SetPriority(UARTRX0_IRQn, priority);
    NVIC_EnableIRQ(UARTRX0_IRQn);
}

void drv_uart_irq_disable(hal_id_t id, bool rx, bool tx)
{
    SYS_LOG("");
    if (rx)
        UART0->CTRL.RX_INT_ENABLE = 0;
    if (tx)
        UART0->CTRL.TX_INT_ENABLE = 0;
}

bool drv_uart_wait_tx_busy(hal_id_t id)
{
    return (bool)UART0->STATE.TX_FULL;
}

bool drv_uart_is_tx_ready(hal_id_t id)
{
    return true;
}

bool drv_uart_is_rx_ready(hal_id_t id)
{
    return (bool)UART0->STATE.RX_FULL;
}
